package org.particlethink.auth;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSignerUtil;
import org.particlethink.auth.exception.TokenExpireException;
import org.particlethink.utils.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 通过 token 获取用户信息
 */
@Component
public class UserTokenWithRedisHandlerAdapter implements UserTokenHandler {

    private static final String defaultUserTokenPrefix = "##material_project_user_token_";

    @Autowired
    protected RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UserLoginConfigProperties userLoginConfigProperties;


    private static final String subject = "https://particlethink.com";

    /**
     * 生成token处理
     *
     * @param user
     * @return 生成的公钥字符串（经过base64编码）
     */
    public final String handleUser(DefaultUser user) {
        String loginToken;
        try {
            loginToken = generatorLoginToken(user);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        redisTemplate.opsForValue().set(userRedisKey(user), loginToken);
        redisTemplate.expire(userRedisKey(user), userLoginConfigProperties.getTokenTimeout(), userLoginConfigProperties.getTokenTimeoutUnit());
        return loginToken;
    }

    private String generatorLoginToken(DefaultUser user) throws IllegalAccessException {
        return JWT.create()
            .setSigner(JWTSignerUtil.hs256(userLoginConfigProperties.getJwtSecret().getBytes(StandardCharsets.UTF_8)))
            .setSubject(subject)
            .addPayloads(ObjectUtils.mapFromInstance(user, DefaultUser.class))
            .setExpiresAt(new Date(new Date().getTime() + userLoginConfigProperties.getTokenTimeoutUnit().convert(userLoginConfigProperties.getTokenTimeout(), TimeUnit.MILLISECONDS)))
            .sign();
    }

    private String userRedisKey(DefaultUser user) {
        return defaultUserTokenPrefix + user.getUser().getUserId() + "##";
    }

    private DefaultUser obtainUser(HttpServletRequest request) {
        String authorization = request.getHeader("Authorization");
        if (authorization == null || !authorization.startsWith("Bearer ")) {
            return null;
        }
        String token = authorization.replace("Bearer ", "");
        JWT jwt = JWTUtil.parseToken(token);
        // todo 后续需要放出来
        if (!jwt.verify(JWTSignerUtil.hs256(userLoginConfigProperties.getJwtSecret().getBytes(StandardCharsets.UTF_8)))) {
            return null;
        }
        Object exp = jwt.getPayload("exp");
        if (exp == null) {
            return null;
        }
        Number expUnix = (Number) exp;
        if (new Date().getTime() / 1000 - expUnix.longValue() > userLoginConfigProperties.getTokenTimeoutUnit().convert(userLoginConfigProperties.getTokenTimeout(), TimeUnit.SECONDS)) {
            throw new TokenExpireException("token expired");
        }

        return jwt.getPayload().getClaimsJson().toBean(DefaultUser.class);
    }

    /**
	 * 身份认证处理
	 *
	 * @param request
	 * @return
	 */
	public final boolean handleRequest(HttpServletRequest request) {
        DefaultUser user = obtainUser(request);
        if (user == null) {
            return false;
        }
        UserInfoAuthenticationToken token = new UserInfoAuthenticationToken(user, request.getHeader("Authorization"));
		SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
		SecurityContextHolder.getContext().setAuthentication(token);
        return true;
	}

}
